<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/%E6%AD%A6%E6%B1%8932x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/%E6%AD%A6%E6%B1%8916x16.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib1/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib1/pace/pace-theme-center-simple.min.css">
  <script src="/lib1/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"example.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="前言任何可以从应用状态中派生出来的值都应该被自动派生出来。 MobX 是一个身经百战的库，它通过运用透明的函数式响应编程（Transparent Functional Reactive Programming，TFRP）使状态管理变得简单和可扩展。 简单直接 编写无模板的极简代码来精准描述出你的意图。要更新一个记录字段？使用熟悉的 JavaScript 赋值就行。要在异步进程中更新数据？不需要特殊">
<meta property="og:type" content="article">
<meta property="og:title" content="Mobx-React">
<meta property="og:url" content="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx-React/index.html">
<meta property="og:site_name" content="Technological Blog">
<meta property="og:description" content="前言任何可以从应用状态中派生出来的值都应该被自动派生出来。 MobX 是一个身经百战的库，它通过运用透明的函数式响应编程（Transparent Functional Reactive Programming，TFRP）使状态管理变得简单和可扩展。 简单直接 编写无模板的极简代码来精准描述出你的意图。要更新一个记录字段？使用熟悉的 JavaScript 赋值就行。要在异步进程中更新数据？不需要特殊">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/image/Mobx-React/zh.flow.png">
<meta property="og:image" content="http://example.com/image/Mobx-React/action-state-view.png">
<meta property="og:image" content="http://example.com/image/Mobx-React/computed-example.png">
<meta property="og:image" content="http://example.com/image/Mobx-React/autorun.png">
<meta property="article:published_time" content="2021-11-30T04:18:39.000Z">
<meta property="article:modified_time" content="2021-12-01T07:45:34.788Z">
<meta property="article:author" content="Li Yudong">
<meta property="article:tag" content="公司技术栈">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://example.com/image/Mobx-React/zh.flow.png">

<link rel="canonical" href="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx-React/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>Mobx-React | Technological Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Technological Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">IT小白的成长之旅</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">18</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">14</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">95</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/tiarmor1" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx-React/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Mobx-React
        </h1>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-11-30 12:18:39" itemprop="dateCreated datePublished" datetime="2021-11-30T12:18:39+08:00">2021-11-30</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-12-01 15:45:34" itemprop="dateModified" datetime="2021-12-01T15:45:34+08:00">2021-12-01</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88/" itemprop="url" rel="index"><span itemprop="name">盒马技术栈</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
        <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p><em>任何可以从应用状态中派生出来的值都应该被自动派生出来。</em></p>
<p>MobX 是一个身经百战的库，它通过运用透明的函数式响应编程（Transparent Functional Reactive Programming，TFRP）使状态管理变得简单和可扩展。</p>
<p><strong>简单直接</strong></p>
<p>编写无模板的极简代码来精准描述出你的意图。要更新一个记录字段？使用熟悉的 JavaScript 赋值就行。要在异步进程中更新数据？不需要特殊的工具，响应性系统会侦测到你所有的变更并把它们传送到其用武之地。</p>
<p><strong>轻松实现最优渲染</strong></p>
<p>所有对数据的变更和使用都会在运行时被追踪到，并构成一个截取所有状态和输出之间关系的依赖树。这样保证了那些依赖于状态的计算只有在真正需要时才会运行，就像 React 组件一样。无需使用记忆化或选择器之类容易出错的次优技巧来对组件进行手动优化。</p>
<p><strong>架构自由</strong></p>
<p>MobX 不会用它自己的规则来限制你，它可以让你在任意 UI 框架之外管理你的应用状态。这样会使你的代码低耦合、可移植和最重要的——容易测试。</p>
<h2 id="一个简单的例子"><a href="#一个简单的例子" class="headerlink" title="一个简单的例子"></a>一个简单的例子</h2><p>那么使用 MobX 的代码是什么样的呢？</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">"react"</span></span><br><span class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">"react-dom"</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeAutoObservable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"><span class="keyword">import</span> &#123; observer &#125; <span class="keyword">from</span> <span class="string">"mobx-react"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 对应用状态进行建模。</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Timer</span> </span>&#123;</span><br><span class="line">    secondsPassed = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    increase() &#123;</span><br><span class="line">        <span class="keyword">this</span>.secondsPassed += <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    reset() &#123;</span><br><span class="line">        <span class="keyword">this</span>.secondsPassed = <span class="number">0</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> myTimer = <span class="keyword">new</span> Timer()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 构建一个使用 observable 状态的“用户界面”。</span></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function">(<span class="params">&#123; timer &#125;</span>) =&gt;</span> (</span><br><span class="line">    &lt;button onClick=&#123;() =&gt; timer.reset()&#125;&gt;已过秒数：&#123;timer.secondsPassed&#125;&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">))</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">ReactDOM.render(&lt;TimerView timer=&#123;myTimer&#125; /</span>&gt;, <span class="built_in">document</span>.body)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 每秒更新一次‘已过秒数：X’中的文本。</span></span><br><span class="line">setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    myTimer.increase()</span><br><span class="line">&#125;, <span class="number">1000</span>)</span><br></pre></td></tr></table></figure>

<p>围绕 React 组件 <code>TimerView</code> 的 <code>observer</code> 包装会自动侦测到依赖于 observable <code>timer.secondsPassed</code> 的渲染——即使这种依赖关系没有被明确定义出来。 响应性系统会负责在未来<em>恰好那个</em>字段被更新的时候将组件重新渲染。</p>
<p>每个事件（<code>onClick</code> 或 <code>setInterval</code>）都会调用一个用来更新 <em>observable 状态</em> <code>myTimer.secondsPassed</code> 的 <em>action</em>（<code>myTimer.create</code> 或 <code>myTimer.reset</code>）。<strong>Observable 状态的变更会被精确地传送到 <code>TimerView</code> 中所有依赖于它们的<em>计算</em>和<em>副作用</em>里。</strong></p>
<p><img src="/../../image/Mobx-React/zh.flow.png" alt="MobX 单向流"></p>
<p>除了适用于上面的例子之外，这个概念图也适用于其他任何使用 MobX 的应用。</p>
<p>如果想通过一个更大的例子来了解 MobX 的核心概念，请参阅 <a href="https://zh.mobx.js.org/the-gist-of-mobx.html" target="_blank" rel="noopener">MobX 精要</a>部分或查看 <a href="https://zh.mobx.js.org/getting-started.html" target="_blank" rel="noopener">10 分钟交互式入门 MobX 和 React</a>。这些博客文章 <a href="https://michel.codes/blogs/ui-as-an-afterthought" target="_blank" rel="noopener">事后再考虑 UI</a> 和 <a href="https://hackernoon.com/how-to-decouple-state-and-ui-a-k-a-you-dont-need-componentwillmount-cc90b787aa37" target="_blank" rel="noopener">怎样解耦状态和 UI （又名：你不需要 componentWillMount）</a>也对 MobX 所提供的心智模型做了非常详细的描述。</p>
<p><a href="https://gum.co/fSocU" target="_blank" rel="noopener">下载 MobX 6 速查表</a></p>
<blockquote>
<p>兄 dei 们，#mobx 用的并不是发布订阅，也不是你爷爷的观察者模式。非也，它用的是一个经过精心设计的、由宇宙力量驱动的 observable 维度传送门。它并不会进行变更侦测。它其实是个带着一把灵魂刀的 20 级灵能师，连劈带砍让你的视图模型屈服于其刀下。</p>
</blockquote>
<blockquote>
<p>在个人项目里用了几周 #mobx 之后把它介绍给了团队，感觉很棒。一半的时间，双倍的快乐</p>
</blockquote>
<blockquote>
<p>简单地说，使用 #mobx 就是一个持续的循环，我不停地嘀咕“这也太简单了，肯定行不通”，结果不断证明我错了。</p>
</blockquote>
<blockquote>
<p>我已经用 MobX 写过大型的应用了，比起之前那个用 Redux 写的，用 MobX 写的更容易阅读，理解起来也容易得多。</p>
</blockquote>
<blockquote>
<p>#mobx 符合我一直以来想要的事物的样子。真是出人意料的简单快速！非常棒！别错过了！</p>
</blockquote>
<h2 id="MobX-主旨"><a href="#MobX-主旨" class="headerlink" title="MobX 主旨"></a>MobX 主旨</h2><p>MobX区分了应用程序中的以下三个概念：</p>
<ol>
<li>State(状态)</li>
<li>Actions(动作)</li>
<li>Derivations(派生)</li>
</ol>
<p>让我们仔细看看下面的这些概念，或者在<a href="https://zh.mobx.js.org/getting-started.html" target="_blank" rel="noopener">10分钟的Mobx和React简介</a>中，您可以通过交互方式逐步深入了解这些概念，并构建一个简单的待办事项列表(Todo List)应用程序。</p>
<h3 id="1-定义-State-并使其可观察"><a href="#1-定义-State-并使其可观察" class="headerlink" title="1. 定义 State 并使其可观察"></a><strong>1. 定义 State 并使其可观察</strong></h3><p><em>State(状态)</em> 是驱动你的应用程序的数据。</p>
<p>通常来说，状态有<em>领域特定状态</em>（比如 Todo List 中的列表项数据）和<em>视图状态</em> （比如当前选中的列表元素）。State 就像保存着数据的电子表格单元格一样。将 State 存储在任何您喜欢的数据结构中：普通对象、数组、类、循环数据结构或引用。这与MobX的工作方式无关。</p>
<p>只要确保所有你想随时间改变的属性都被标记为<code>observable</code>，这样MobX就可以跟踪它们。以下是一个简单的示例：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; makeObservable, observable, action &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo</span> </span>&#123;</span><br><span class="line">    id = <span class="built_in">Math</span>.random()</span><br><span class="line">    title = <span class="string">""</span></span><br><span class="line">    finished = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(title) &#123;</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            title: observable,</span><br><span class="line">            finished: observable,</span><br><span class="line">            toggle: action</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">this</span>.title = title</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    toggle() &#123;</span><br><span class="line">        <span class="keyword">this</span>.finished = !<span class="keyword">this</span>.finished</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>提示</strong>: 在这个例子中我们可以用 <a href="https://zh.mobx.js.org/observable-state.html" target="_blank" rel="noopener"><code>makeAutoObservable</code></a> 对其进行简化，但是为了能更详细的展示不同的概念，我们对其进行显式设置。</p>
<p>使用 <code>observable</code> 就像将对象的属性放在Excel表格的单元格中。但是和单元格不同的是，他们的值不仅仅是数值，也可以是引用、对象和数组。接下来我们看一下被我们标记为 <code>action</code> 的<code>toggle</code>。</p>
<h3 id="2-使用-Action-更新-State"><a href="#2-使用-Action-更新-State" class="headerlink" title="2. 使用 Action 更新 State"></a><strong>2. 使用 Action 更新 State</strong></h3><p><em>Action(动作)</em> 是任意可以改变 <em>State(状态)</em> 的代码，比如用户事件处理、后端推送数据处理、调度器事件处理等等。Action 就像用户在Excel单元格中输入了新的值。</p>
<p>在 <code>Todo</code> 类中，我们可以看到 <code>toggle</code> 方法改变了 <code>finished</code> 属性的值，而 <code>finished</code> 是被标记为 <code>observable</code> 的。建议您将所有修改 <code>observable</code> 值的代码标记为 <a href="https://zh.mobx.js.org/actions.html" target="_blank" rel="noopener"><code>action</code></a>。MobX 可以自动进行事务处理以轻松实现最佳性能。使用 Action 可以帮助您更好地组织代码，并防止您在无意中修改 State。</p>
<p>在 MobX 术语中，可以修改 State 的方法被称为 <em>action(动作)</em> 。这与基于当前状态来生成新信息的 <em>view(视图)</em> 是不同的。 您代码中的每一个方法只应完成上述两个目标中的一个。</p>
<h3 id="3-创建-Derivations-以便自动对-State-变化进行响应"><a href="#3-创建-Derivations-以便自动对-State-变化进行响应" class="headerlink" title="3. 创建 Derivations 以便自动对 State 变化进行响应"></a><strong>3. 创建 Derivations 以便自动对 State 变化进行响应</strong></h3><p><em>任何</em> 来源是<em>State(状态)</em> 并且不需要进一步交互的东西都是 Derivation(派生)。</p>
<p>Derivations 包括许多方式:</p>
<ul>
<li><em>用户界面</em></li>
<li><em>派生数据</em> , 比如剩余未完成<code>todos</code>的数量</li>
<li><em>后端集成</em> , 比如发送改变到服务器端</li>
</ul>
<p>Mobx 区分了两种 Derivation :</p>
<ul>
<li><em>Computed values</em>,总是可以通过纯函数从当前的可观测 State 中派生。</li>
<li><em>Reactions</em>, 当 State 改变时需要自动运行的副作用 (命令式编程和响应式编程之间的桥梁)</li>
</ul>
<p>当最开始使用MobX时，人们容易过度使用 <em>Reaction</em>。</p>
<p>*<em>黄金法则是，如果要基于当前 State 创建值，请始终使用 <em>computed</em>。</em>*</p>
<h4 id="3-1-通过-computed-对派生值进行建模"><a href="#3-1-通过-computed-对派生值进行建模" class="headerlink" title="3.1. 通过 computed 对派生值进行建模"></a>3.1. 通过 computed 对派生值进行建模</h4><p>你可以通过定义 getter 方法并使用 <code>makeObservable</code> 将其标记为 <code>computed</code> 的方式创建一个 <em>computed</em> 值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; makeObservable, observable, computed &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TodoList</span> </span>&#123;</span><br><span class="line">    todos = []</span><br><span class="line">    <span class="keyword">get</span> unfinishedTodoCount() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.todos.filter(<span class="function"><span class="params">todo</span> =&gt;</span> !todo.finished).length</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">constructor</span>(todos) &#123;</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            todos: observable,</span><br><span class="line">            unfinishedTodoCount: computed</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">this</span>.todos = todos</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Mobx 会确保 <code>unfinishedTodoCount</code> 会在todos数组发生变化中或者 todos中的一个对象中的 <code>finished</code>属性被修改时自动更新。</p>
<p>这些计算类似于 Excel 单元格中的公式。它们仅在需要时自动更新。也就是说，如果有观察者使用其结果时才会更新。也就是说，如果有有人关心其结果时才会更新。</p>
<h4 id="3-2-使用-reaction-对副作用建模"><a href="#3-2-使用-reaction-对副作用建模" class="headerlink" title="3.2. 使用 reaction 对副作用建模"></a>3.2. 使用 reaction 对副作用建模</h4><p>作为用户，要想在屏幕上看到状态或计算值的变化，就需要一个重新绘制部分GUI的 <em>reactions</em> 。</p>
<p>Reaction 和 computed 类似，但并不产生信息，而是产生副作用，如打印到控制台、发出网络请求、增量更新 React 组件树以便更新DOM等。简而言之，<em>reaction</em> 是 <a href="https://en.wikipedia.org/wiki/Reactive_programming" target="_blank" rel="noopener">响应式编程</a>和<a href="https://en.wikipedia.org/wiki/Imperative_programming" target="_blank" rel="noopener">指令式编程</a>之间的桥梁。</p>
<p>到目前为止，最常用的 reaction 形式是UI组件。 注意，action 和 reaction 都可能引起副作用。 副作用应有一个清晰的、显式的起源，例如在提交表单时发出网络请求，应该从相关的事件处理程序显式触发。</p>
<h4 id="3-3-响应式-React-组件"><a href="#3-3-响应式-React-组件" class="headerlink" title="3.3. 响应式 React 组件"></a>3.3. 响应式 React 组件</h4><p>如果使用 React，你可以将组件用<a href="https://zh.mobx.js.org/installation.html#installation" target="_blank" rel="noopener">安装</a>中下载的包中的<a href="https://zh.mobx.js.org/react-integration.html" target="_blank" rel="noopener"><code>observer</code></a>函数来包装起来，以便让组件成为响应式的。在这个示例中，我们将用更轻量的 <code>mobx-react-lite</code> 包。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span></span><br><span class="line"><span class="keyword">import</span> &#123; render &#125; <span class="keyword">from</span> <span class="string">"react-dom"</span></span><br><span class="line"><span class="keyword">import</span> &#123; observer &#125; <span class="keyword">from</span> <span class="string">"mobx-react-lite"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TodoListView = observer(<span class="function">(<span class="params">&#123; todoList &#125;</span>) =&gt;</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">        &lt;ul&gt;</span><br><span class="line">            &#123;todoList.todos.map(<span class="function"><span class="params">todo</span> =&gt;</span> (</span><br><span class="line">                &lt;TodoView todo=&#123;todo&#125; key=&#123;todo.id&#125; /&gt;</span><br><span class="line">            ))&#125;</span><br><span class="line">        &lt;<span class="regexp">/ul&gt;</span></span><br><span class="line"><span class="regexp">        Tasks left: &#123;todoList.unfinishedTodoCount&#125;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>div&gt;</span><br><span class="line">))</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TodoView = observer(<span class="function">(<span class="params">&#123; todo &#125;</span>) =&gt;</span> (</span><br><span class="line">    &lt;li&gt;</span><br><span class="line">        &lt;input type=<span class="string">"checkbox"</span> checked=&#123;todo.finished&#125; onClick=&#123;() =&gt; todo.toggle()&#125; /&gt;</span><br><span class="line">        &#123;todo.title&#125;</span><br><span class="line">    &lt;<span class="regexp">/li&gt;</span></span><br><span class="line"><span class="regexp">))</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const store = new TodoList([new Todo("Get Coffee"), new Todo("Write simpler code")])</span></span><br><span class="line"><span class="regexp">render(&lt;TodoListView todoList=&#123;store&#125; /</span>&gt;, <span class="built_in">document</span>.getElementById(<span class="string">"root"</span>))</span><br></pre></td></tr></table></figure>

<p><code>observer</code> 将 React 组件转化为了从数据到渲染的派生过程。 当使用 MobX 的时，不存在“智能组件”和“哑巴组件”。所有的组件在渲染时都是智能的，但是在定义时是按照哑巴组件的方式去定义的。MobX会简单确定这个组件是否需要进行重绘，并止步于此。</p>
<p>因此，上述示例中的<code>onClick</code>事件处理器调用<code>toggle</code> Action 后，会使对应的<code>TodoView</code>组件重绘，但仅当未完成任务的数量发生更改时才会使 <code>TodoListView</code> 组件重绘。</p>
<p>如果移除了<code>Tasks left</code>这行代码（或者把他拆分到另一个组件中）,<code>TodoListView</code>组件就不再 <code>toggle</code> 执行时产生重绘了。您可以查阅<a href="https://zh.mobx.js.org/react-integration.html" target="_blank" rel="noopener">与 React 集成</a>来了解更多有关 React 是如何与 MobX 协同运作的。</p>
<h4 id="3-4-自定义-Reaction"><a href="#3-4-自定义-Reaction" class="headerlink" title="3.4. 自定义 Reaction"></a>3.4. 自定义 Reaction</h4><p>通常情况下你不需要使用它们，可以使用 <a href="https://zh.mobx.js.org/reactions.html#autorun" target="_blank" rel="noopener"><code>autorun</code></a> ,<a href="https://zh.mobx.js.org/reactions.html#reaction" target="_blank" rel="noopener"><code>reaction</code></a> 或 <a href="https://zh.mobx.js.org/reactions.html#when" target="_blank" rel="noopener"><code>when</code></a> 方法来订制你的特殊业务场景。</p>
<p>比如，下面的 <code>autorun</code> 将在<code>unfinishedTodoCount</code>的数量发生变化时输出日志。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 一个自动观察state的函数</span></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Tasks left: "</span> + todos.unfinishedTodoCount)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>为什么每次 <code>unfinishedTodoCount</code>发生改变时都会输出日志信息呢？答案是以下法则：</p>
<p><em>MobX对在执行跟踪函数期间读取的任何现有可观察属性作出响应</em>。</p>
<p>要了解更多关于MobX如何确定需要对哪些可观察对象作出响应的信息，请查看 <a href="https://zh.mobx.js.org/understanding-reactivity.html" target="_blank" rel="noopener">理解响应性</a> 章节。</p>
<h3 id="原则"><a href="#原则" class="headerlink" title="原则"></a>原则</h3><p>Mobx 使用单向数据流，利用 <em>action</em> 改变 <em>state</em> ，进而更新所有受影响的 <em>view</em></p>
<p><img src="/../../image/Mobx-React/action-state-view.png" alt="Action, State, View"></p>
<ol>
<li>所有的 <em>derivations</em> 将在 <em>state</em> 改变时<strong>自动且原子化地更新</strong>。因此不可能观察中间值。</li>
<li>所有的 <em>dervations</em> 默认将会<strong>同步</strong>更新，这意味着 <em>action</em> 可以在 <em>state</em> 改变 之后安全的直接获得 computed 值。</li>
<li><em>computed value</em> 的更新是<strong>惰性</strong>的，任何 computed value 在需要他们的副作用发生之前都是不激活的。</li>
<li>所有的 <em>computed value</em> 都应是<strong>纯函数</strong>,他们不应该修改 <em>state</em>。</li>
</ol>
<p>想了解更多背景，请查阅 <a href="https://hackernoon.com/the-fundamental-principles-behind-mobx-7a725f71f3e8" target="_blank" rel="noopener">MobX背后的基本原则</a></p>
<h1 id="Mobx-Core"><a href="#Mobx-Core" class="headerlink" title="Mobx Core"></a>Mobx Core</h1><h2 id="创建可观察状态"><a href="#创建可观察状态" class="headerlink" title="创建可观察状态"></a>创建可观察状态</h2><p>属性，完整的对象，数组，Maps 和 Sets 都可以被转化为可观察对象。 使得对象可观察的基本方法是使用 <code>makeObservable</code> 为每个属性指定一个注解。 最重要的注解如下：</p>
<ul>
<li><code>observable</code> 定义一个存储 state 的可追踪字段。</li>
<li><code>action</code> 将一个方法标记为可以修改 state 的 action。</li>
<li><code>computed</code> 标记一个可以由 state 派生出新的值并且缓存其输出的 getter。</li>
</ul>
<p>像数组，Maps 和 Sets 这样的集合都将被自动转化为可观察对象。</p>
<p><strong><code>makeObservable</code></strong>`</p>
<p>用法：</p>
<ul>
<li><code>makeObservable(target, annotations?, options?)</code></li>
</ul>
<p>这个函数可以捕获<em>已经存在</em>的对象属性并且使得它们可观察。任何 JavaScript 对象（包括类的实例）都可以作为 <code>target</code> 被传递给这个函数。 一般情况下，<code>makeObservable</code> 是在类的构造函数中调用的，并且它的第一个参数是 <code>this</code> 。 <code>annotations</code> 参数将会为每一个成员映射 <a href="https://zh.mobx.js.org/observable-state.html#%E5%8F%AF%E7%94%A8%E7%9A%84%E6%B3%A8%E8%A7%A3" target="_blank" rel="noopener">注解</a>。需要注意的是，当使用 <a href="https://zh.mobx.js.org/enabling-decorators.html" target="_blank" rel="noopener">装饰器</a> 时，<code>annotations</code> 参数将会被忽略。</p>
<p>派生数据并且接受参数的方法（例如：<code>findUsersOlderThan(age: number): User[]</code>）不需要任何注解。 当我们从一个 reaction 中调用它们时，它们的读取操作仍然会被跟踪，但是为了避免内存泄漏，它们的输出将不会被记忆化。更详细的信息可以查看 <a href="https://github.com/mobxjs/mobx-utils#computedfn" target="_blank" rel="noopener">MobX-utils computedFn {🚀}</a>。Mobx 通过 <code>override</code> 注解 <a href="https://zh.mobx.js.org/subclassing.html" target="_blank" rel="noopener">支持子类的使用，但会有一些局限性</a>。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//class + makeObservable</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeObservable, observable, computed, action &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Doubler</span> </span>&#123;</span><br><span class="line">    value</span><br><span class="line">    <span class="keyword">constructor</span>(value) &#123;</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            value: observable,</span><br><span class="line">            double: computed,</span><br><span class="line">            increment: action,</span><br><span class="line">            fetch: flow</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">this</span>.value = value</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">get</span> double() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.value * <span class="number">2</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    increment() &#123;</span><br><span class="line">        <span class="keyword">this</span>.value++</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    *fetch() &#123;</span><br><span class="line">        <span class="keyword">const</span> response = <span class="keyword">yield</span> fetch(<span class="string">"/api/value"</span>)</span><br><span class="line">        <span class="keyword">this</span>.value = response.json()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*所有带注解 的字段都是 不可配置的。</span></span><br><span class="line"><span class="comment">所有的不可观察（无状态）的字段（action, flow）都是 不可写的*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//factory function + makeAutoObservable</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeAutoObservable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createDoubler</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> makeAutoObservable(&#123;</span><br><span class="line">        value,</span><br><span class="line">        <span class="keyword">get</span> double() &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.value * <span class="number">2</span></span><br><span class="line">        &#125;,</span><br><span class="line">        increment() &#123;</span><br><span class="line">            <span class="keyword">this</span>.value++</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//注意，类也可以跟 makeAutoObservable 合用。 示例中的差异就展示了将 MobX 应用于不同编程风格的方法。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//observable</span></span><br><span class="line"><span class="keyword">import</span> &#123; observable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> todosById = observable(&#123;</span><br><span class="line">    <span class="string">"TODO-123"</span>: &#123;</span><br><span class="line">        title: <span class="string">"find a decent task management system"</span>,</span><br><span class="line">        done: <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">todosById[<span class="string">"TODO-456"</span>] = &#123;</span><br><span class="line">    title: <span class="string">"close all tickets older than two weeks"</span>,</span><br><span class="line">    done: <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> tags = observable([<span class="string">"high prio"</span>, <span class="string">"medium prio"</span>, <span class="string">"low prio"</span>])</span><br><span class="line">tags.push(<span class="string">"prio: for fun"</span>)</span><br><span class="line"><span class="comment">/*与第一个例子中的 makeObservable 不同，observable 支持为对象添加（和删除）字段。 这使得 observable 非常适合用于像动态键控的对象、数组、Maps 和 Sets 之类的集合。*/</span></span><br></pre></td></tr></table></figure>

<p><strong><code>makeAutoObservable</code></strong></p>
<p>使用：</p>
<ul>
<li><code>makeAutoObservable(target, overrides?, options?)</code></li>
</ul>
<p><code>makeAutoObservable</code> 就像是加强版的 <code>makeObservable</code>，在默认情况下它将推断所有的属性。你仍然可以使用 <code>overrides</code> 重写某些注解的默认行为。 具体来说，<code>false</code> 可用于从自动处理中排除一个属性或方法。 查看上面的代码分页获取示例。 与使用 <code>makeObservable</code> 相比，<code>makeAutoObservable</code> 函数更紧凑，也更容易维护，因为新成员不需要显式地提及。 然而，<code>makeAutoObservable</code> 不能被用于带有 super 的类或 <a href="https://zh.mobx.js.org/subclassing.html" target="_blank" rel="noopener">子类</a>。</p>
<p>推断规则：</p>
<ul>
<li>所有 <em>自有</em> 属性都成为 <code>observable</code>。</li>
<li>所有 <code>get</code>ters 都成为 <code>computed</code>。</li>
<li>所有 <code>set</code>ters 都成为 <code>action</code>。</li>
<li>所有 <em>prototype 中的 functions</em> 都成为 <code>autoAction</code>。</li>
<li>所有 <em>prototype 中的 generator functions</em> 都成为 <code>flow</code>。（需要注意，generators 函数在某些编译器配置中无法被检测到，如果 flow 没有正常运行，请务必明确地指定 <code>flow</code> 注解。）</li>
<li>在 <code>overrides</code> 参数中标记为 <code>false</code> 的成员将不会被添加注解。例如，将其用于像标识符这样的只读字段。</li>
</ul>
<p><strong><code>observable</code></strong></p>
<p>用法：</p>
<ul>
<li><code>observable(source, overrides?, options?)</code></li>
</ul>
<p><code>observable</code> 注解可以作为一个函数进行调用，从而一次性将整个对象变成可观察的。 <code>source</code> 对象将会被克隆并且所有的成员都将会成为可观察的，类似于 <code>makeAutoObservable</code> 做的那样。 同样，你可以传入一个 <code>overrides</code> 对象来为特定的成员提供特定的注解。 查看上面的代码获取示例。</p>
<p>由 <code>observable</code> 返回的对象将会使用 Proxy 包装，这意味着之后被添加到这个对象中的属性也将被侦测并使其转化为可观察对象（除非禁用 <a href="https://zh.mobx.js.org/configuration.html#proxy-%E9%80%89%E9%A1%B9" target="_blank" rel="noopener">proxy</a>）。</p>
<p><code>observable</code> 方法也可以被像 <a href="https://zh.mobx.js.org/api.html#observablearray" target="_blank" rel="noopener">arrays</a>，<a href="https://zh.mobx.js.org/api.html#observablemap" target="_blank" rel="noopener">Maps</a> 和 <a href="https://zh.mobx.js.org/api.html#observableset" target="_blank" rel="noopener">Sets</a> 这样的集合调用。这些集合也将被克隆并转化为可观察对象。</p>
<h3 id="可用的注解"><a href="#可用的注解" class="headerlink" title="可用的注解"></a>可用的注解</h3><table>
<thead>
<tr>
<th>注解</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td><code>observable</code> <code>observable.deep</code></td>
<td>定义一个存储 state 的可跟踪字段。如果可能，任何被赋值给 <code>observable</code> 的字段都会基于它自己的类型被（深度）转化为<code>observable</code>、<code>autoAction</code> 或 <code>flow</code>。只有 <code>plain object</code>、<code>array</code>、<code>Map</code>、<code>Set</code>、<code>function</code>、<code>generator function</code> 可以转换，类实例和其他实例不会被影响。</td>
</tr>
<tr>
<td><code>observable.ref</code></td>
<td>类似于 <code>observable</code>，但只有重新赋值才会被追踪。所赋的值会被完全忽略，并且将不会主动转化为 <code>observable</code>/<code>autoAction</code>/<code>flow</code>。比方说，在你打算将不可变数据存储在可观察字段中时，可以使用这个注解。</td>
</tr>
<tr>
<td><code>observable.shallow</code></td>
<td>类似于 <code>observable.ref</code> 但是是用于集合的。任何所赋的集合都会被转化为可观察值，但是其内部的值并不会变为可观察值。</td>
</tr>
<tr>
<td><code>observable.struct</code></td>
<td>类似于 <code>observable</code>，但是会忽略所赋的值中所有在结构上与当前值相同的值。</td>
</tr>
<tr>
<td><code>action</code></td>
<td>把一个函数标记为会修改 state 的 action。查看 <a href="https://zh.mobx.js.org/actions.html" target="_blank" rel="noopener">actions</a> 获取更多信息。不可写。</td>
</tr>
<tr>
<td><code>action.bound</code></td>
<td>类似于 action，但是会将 action 绑定到实例，因此将始终设置 <code>this</code>。不可写。</td>
</tr>
<tr>
<td><code>computed</code></td>
<td>可以用在 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get" target="_blank" rel="noopener">getter</a> 上，用来将其声明为可缓存的派生值。查看 <a href="https://zh.mobx.js.org/computeds.html" target="_blank" rel="noopener">computeds</a> 获取更多信息。</td>
</tr>
<tr>
<td><code>computed.struct</code></td>
<td>类似于 <code>computed</code>，但如果重新计算后的结果在结构上与之前的结果相等，那么观察者将不会收到通知。</td>
</tr>
<tr>
<td><code>true</code></td>
<td>推断最佳注解。查看 <a href="https://zh.mobx.js.org/observable-state.html#makeautoobservable" target="_blank" rel="noopener">makeAutoObservable</a> 获取更多信息。</td>
</tr>
<tr>
<td><code>false</code></td>
<td>刻意不为该属性指定注解。</td>
</tr>
<tr>
<td><code>flow</code></td>
<td>创建一个 <code>flow</code> 管理异步进程。查看 <a href="https://zh.mobx.js.org/actions.html#%E4%BD%BF%E7%94%A8-flow-%E4%BB%A3%E6%9B%BF-async--await-" target="_blank" rel="noopener">flow</a> 获取更多信息。需要注意的是，推断出来的 TypeScript 返回类型可能会出错。 不可写。</td>
</tr>
<tr>
<td><code>flow.bound</code></td>
<td>类似于 flow, 但是会将 flow 绑定到实例，因此将始终设置 <code>this</code>。 不可写。</td>
</tr>
<tr>
<td><code>override</code></td>
<td><a href="https://zh.mobx.js.org/subclassing.html" target="_blank" rel="noopener">用于子类覆盖继承的 <code>action</code>，<code>flow</code>，<code>computed</code>，<code>action.bound</code></a>。</td>
</tr>
<tr>
<td><code>autoAction</code></td>
<td>不应被显式调用，但 <code>makeAutoObservable</code> 内部会对其进行调用，以便根据调用上下文将方法标识为 action 或者派生值。</td>
</tr>
</tbody></table>
<h3 id="局限性"><a href="#局限性" class="headerlink" title="局限性"></a>局限性</h3><ol>
<li><code>make(Auto)Observable</code> 仅支持已经定义的属性。请确保你的 <a href="https://zh.mobx.js.org/installation.html#%E5%AF%B9%E7%B1%BB%E5%B1%9E%E6%80%A7%E4%BD%BF%E7%94%A8%E7%AC%A6%E5%90%88%E8%A7%84%E8%8C%83%E7%9A%84%E8%BD%AC%E6%8D%A2" target="_blank" rel="noopener"><strong>编译器选项</strong>是正确的</a>，或者，作为权宜之计，确保在你使用 <code>make(Auto)Observable</code> 之前已经为所有属性赋了值。如果没有正确的配置，已经声明而未初始化的字段（例如：<code>class X { y; }</code>）将无法被正确侦测到。</li>
<li><code>makeObservable</code> 只能注解由其本身所在的类定义声明出来的属性。如果一个子类或超类引入了可观察字段，那么该子类或超类就必须自己为那些属性调用 <code>makeObservable</code>。</li>
<li><code>options</code> 参数只能提供一次。被传入的 <code>options</code> 是 <em>“有粘性”</em> 的，之后无法更改（例如，在 <a href="https://zh.mobx.js.org/subclassing.html" target="_blank" rel="noopener">子类</a> 中）。</li>
<li><strong>每个字段只能被注解一次</strong>（<code>override</code> 除外）。字段注解和配置不能在 <a href="https://zh.mobx.js.org/subclassing.html" target="_blank" rel="noopener">子类</a> 中改变。</li>
<li>非普通对象（<strong>类</strong>）中的 <strong>所有被注解过的</strong> 字段都是 <strong>不可配置的</strong>。<br><a href="https://zh.mobx.js.org/configuration.html#safedescriptors-boolean" target="_blank" rel="noopener">可以通过 <code>configure({ safeDescriptors: false })</code> 来禁用 {🚀☣️} </a>。</li>
<li><strong>所有不可观察</strong>（stateless）字段（<code>action</code>，<code>flow</code>）都是 <strong>不可写的</strong>。<br><a href="https://zh.mobx.js.org/configuration.html#safedescriptors-boolean" target="_blank" rel="noopener">可以通过 <code>configure({ safeDescriptors: false })</code> 来禁用 {🚀☣️} </a>。</li>
<li><a href="https://zh.mobx.js.org/subclassing.html" target="_blank" rel="noopener">只有定义在<strong>原型</strong>上的 <strong><code>action</code>，<code>computed</code>，<code>flow</code>，<code>action.bound</code></strong> 可以在子类中被 <strong>overriden</strong></a>。</li>
<li>默认情况下 <em>TypeScript</em> 不会允许你注解<strong>私有</strong>字段。这个问题可以通过将相关私有字段作为泛型参数显式传入来解决，就像这样： <code>makeObservable&lt;MyStore, &quot;privateField&quot; | &quot;privateField2&quot;&gt;(this, { privateField: observable, privateField2: observable })</code>。</li>
<li><strong>make(Auto)Observable</strong>的调用和注解的提供必须无条件地进行，因为这样才可能对推断结果进行缓存。</li>
<li><strong>不支持</strong> 在调用 <strong><code>make(Auto)Observable</code></strong> 之后 <strong>修改原型</strong>。</li>
<li><strong>不支持</strong> <em>EcmaScript</em> 中的<strong>私有</strong>字段（**<code>#field</code>**）。使用 <em>TypeScript</em> 时，推荐改用 <code>private</code> 修饰符。</li>
<li><strong>不支持</strong> 在单个继承链中 <strong>混合使用注解和装饰器</strong> - 例如，在超类中使用了装饰器，就不能再在子类中使用注解。</li>
<li><code>makeObservable</code>，<code>extendObservable</code> 不能在其它内置可观察类型上使用（<code>ObservableMap</code>，<code>ObservableSet</code>，<code>ObservableArray</code> 等）。</li>
<li><code>makeObservable(Object.create(prototype))</code> 将属性从 <code>prototype</code> 拷贝到新创建的对象并且使得其是可观察的。此行为是错误的、不可预测的，因此已经<strong>不推荐使用</strong>，并可能会在未来有所变动。不要使用它。</li>
</ol>
<h3 id="Options-🚀"><a href="#Options-🚀" class="headerlink" title="Options {🚀}"></a>Options {🚀}</h3><p>上面的 API 都有一个可选的 <code>options</code> 参数，该参数是一个对象，支持以下选项：</p>
<ul>
<li><strong><code>autoBind: true</code></strong> 默认使用 <code>action.bound</code>/<code>flow.bound</code>，而不使用 <code>action</code>/<code>flow</code>。不影响被显式注释过的成员。</li>
<li><strong><code>deep: false</code></strong> 默认使用 <code>observable.ref</code>，而不使用 <code>observable</code>。不影响被显式注释过的成员。</li>
<li><strong><code>name: &lt;string&gt;</code></strong> 为对象提供一个调试名称，该名称将被打印在错误消息和 reflection API 中。</li>
<li><strong><code>proxy: false</code></strong> 迫使 <code>observable(thing)</code> 使用非 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" target="_blank" rel="noopener"><strong>proxy</strong></a> 的实现。如果对象的结构不会随着时间变化，那么这就是一个很好的选择，因为非代理对象更容易调试并且速度更快。请参见 <a href="https://zh.mobx.js.org/observable-state.html#avoid-proxies" target="_blank" rel="noopener">避免代理</a>。</li>
</ul>
<p><strong>将 observable 转换回普通的 JavaScript 集合</strong></p>
<p>有时有必要将可观察的数据结构转换回原生的数据结构。 例如，将可观察对象传入一个无法跟踪可观察对象的 React 组件时，或者想要获取一个不会再被更改的副本时。</p>
<p>要进行浅转换，用常用的 JavaScript 操作就可以做到：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> plainObject = &#123; ...observableObject &#125;</span><br><span class="line"><span class="keyword">const</span> plainArray = observableArray.slice()</span><br><span class="line"><span class="keyword">const</span> plainMap = <span class="keyword">new</span> <span class="built_in">Map</span>(observableMap)</span><br></pre></td></tr></table></figure>

<p>要将数据树递归地转换为普通对象，可使用 <a href="https://zh.mobx.js.org/api.html#tojs" target="_blank" rel="noopener"><code>toJS</code></a> 工具函数。 对于类，建议实现一个 <code>toJSON()</code> 方法，因为这样会被 <code>JSON.stringify</code> 识别出来。</p>
<p><strong>关于类的说明</strong></p>
<p>到目前为止，以上大多数示例都倾向于使用类进行构建。 MobX 原则上对此没有限制，而且可能有同样多的MobX 用户使用的是普通对象。 但是，使用类的一个好处是更容易被索引以实现自动补全等功能，例如使用 TypeScript。 另外，<code>instanceof</code> 检查对于类型推断来说非常强大，并且类实例不会被包装在 <code>Proxy</code> 对象中，这一点给了它们更好的调试体验。 最后，使用类会从引擎优化中受益良多，因为它们的形态是可预测的并且方法在原型上是共享的。 但是，复杂的继承模式很容易给您自己带来不必要的麻烦，因此如果您想使用类，请尽量使其保持简单。 因此，尽管稍微倾向于使用类，但如果有更适合您的，我们肯定会鼓励您不采用这种风格。</p>
<h2 id="使用-actions-更新-state"><a href="#使用-actions-更新-state" class="headerlink" title="使用 actions 更新 state"></a>使用 actions 更新 state</h2><p>用法：</p>
<ul>
<li><code>action</code> <em>（注解）</em></li>
<li><code>action(fn)</code></li>
<li><code>action(name, fn)</code></li>
</ul>
<p>所有的应用程序都有 actions。action 就是任意一段修改 state 的代码。原则上，actions 总会为了对一个事件做出响应而发生。例如，点击了一个按钮，一些输入被改变了，一个 websocket 消息被送达了，等等。</p>
<p>尽管 <a href="https://zh.mobx.js.org/observable-state.html#makeautoobservable" target="_blank" rel="noopener"><code>makeAutoObservable</code></a> 可以自动帮你声明一部分 actions，但是 MobX 还是要求你声明你的 actions。Actions 可以帮助你更好的组织你的代码并提供以下性能优势：</p>
<ol>
<li>它们在 <a href="https://zh.mobx.js.org/api.html#transaction" target="_blank" rel="noopener">transactions</a> 内部运行。任何可观察对象在最外层的 action 完成之前都不会被更新，这一点保证了在 action 完成之前，action 执行期间生成的中间值或不完整的值对应用程序的其余部分都是不可见的。</li>
<li>默认情况下，不允许在 actions 之外改变 state。这有助于在代码中清楚地对状态更新发生的位置进行定位。</li>
</ol>
<p><code>action</code> 注解应该仅用于会<em>修改</em> state 的函数。派生其他信息（执行查询或者过滤数据）的函数<em>不应该</em>被标记为 actions，以便 MobX 可以对它们的调用进行跟踪。 带有 <code>action</code> 注解的成员是不可枚举的。</p>
<p><strong>例子</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//makeObservable</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeObservable, observable, action &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Doubler</span> </span>&#123;</span><br><span class="line">    value = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(value) &#123;</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            value: observable,</span><br><span class="line">            increment: action</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    increment() &#123;</span><br><span class="line">        <span class="comment">// 观察者不会看到中间状态.</span></span><br><span class="line">        <span class="keyword">this</span>.value++</span><br><span class="line">        <span class="keyword">this</span>.value++</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//makeAutoObservable</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeAutoObservable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Doubler</span> </span>&#123;</span><br><span class="line">    value = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(value) &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    increment() &#123;</span><br><span class="line">        <span class="keyword">this</span>.value++</span><br><span class="line">        <span class="keyword">this</span>.value++</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//action.bound</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeObservable, observable, action &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Doubler</span> </span>&#123;</span><br><span class="line">    value = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(value) &#123;</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            value: observable,</span><br><span class="line">            increment: action.bound</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    increment() &#123;</span><br><span class="line">        <span class="keyword">this</span>.value++</span><br><span class="line">        <span class="keyword">this</span>.value++</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> doubler = <span class="keyword">new</span> Doubler()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这样调用 increment 是安全的, 因为它已经被绑定了。</span></span><br><span class="line">setInterval(doubler.increment, <span class="number">1000</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//action(fn)</span></span><br><span class="line"><span class="keyword">import</span> &#123; observable, action &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> state = observable(&#123; <span class="attr">value</span>: <span class="number">0</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> increment = action(<span class="function"><span class="params">state</span> =&gt;</span> &#123;</span><br><span class="line">    state.value++</span><br><span class="line">    state.value++</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">increment(state)</span><br><span class="line"></span><br><span class="line"><span class="comment">//runInAction(fn)</span></span><br><span class="line"><span class="keyword">import</span> &#123; observable, runInAction &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> state = observable(&#123; <span class="attr">value</span>: <span class="number">0</span> &#125;)</span><br><span class="line"></span><br><span class="line">runInAction(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    state.value++</span><br><span class="line">    state.value++</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>



<h3 id="使用-action-包装函数"><a href="#使用-action-包装函数" class="headerlink" title="使用 action 包装函数"></a>使用 <code>action</code> 包装函数</h3><p>为了尽可能地利用 MobX 的事务性，actions 应该尽可能被传到外围。如果一个类方法会修改 state，可以将其标记为 action。把事件处理函数标记为 actions 就更好了，因为最外层的事务起着决定性作用。一个未被标记的、会接着调用两个 actions 的事件处理函数仍然将会生成两个事务。</p>
<p>为了帮助创建基于 action 的事件处理函数，<code>action</code> 不仅仅是一个注解，更是一个高阶函数。可以使用函数将它作为一个参数来调用，在这种情况下它将会返回一个有着相同签名的使用 <code>action</code> 包装过的函数。</p>
<p>例如在 React 中，可以按照下面的方式包装 <code>onClick</code> 事件处理函数。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ResetButton = <span class="function">(<span class="params">&#123; formState &#125;</span>) =&gt;</span> (</span><br><span class="line">    &lt;button</span><br><span class="line">        onClick=&#123;action(<span class="function"><span class="params">e</span> =&gt;</span> &#123;</span><br><span class="line">            formState.resetPendingUploads()</span><br><span class="line">            formState.resetValues()</span><br><span class="line">            e.stopPropagation()</span><br><span class="line">        &#125;)&#125;</span><br><span class="line">    &gt;</span><br><span class="line">        Reset form</span><br><span class="line">    &lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>

<p>为了更好的调试体验，我们推荐为被包装的函数命名，或者将名称作为 <code>action</code> 的第一个参数进行传递。</p>
<p><strong><code>action.bound</code></strong></p>
<p>用法：</p>
<ul>
<li><code>action.bound</code> <em>（注解）</em></li>
</ul>
<p><code>action.bound</code> 注解可用于将方法自动绑定到正确的实例，这样 <code>this</code> 会始终被正确绑定在函数内部。</p>
<p><strong><code>runInAction</code></strong></p>
<p>用法：</p>
<ul>
<li><code>runInAction(fn)</code></li>
</ul>
<p>使用这个工具函数来创建一个会被立即调用的临时 action。在异步进程中非常有用。 请查看 <a href="https://zh.mobx.js.org/actions.html#%E4%BE%8B%E5%AD%90" target="_blank" rel="noopener">上面代码块</a> 中的实例。</p>
<h3 id="Actions-和继承"><a href="#Actions-和继承" class="headerlink" title="Actions 和继承"></a>Actions 和继承</h3><p>只有定义在<strong>原型</strong>上的函数可以被子类<strong>覆盖</strong>：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">    <span class="comment">// on instance</span></span><br><span class="line">    arrowAction = <span class="function"><span class="params">()</span> =&gt;</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// on prototype</span></span><br><span class="line">    action() &#123;&#125;</span><br><span class="line">    boundAction() &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            arrowAction: action</span><br><span class="line">            action: action,</span><br><span class="line">            boundAction: action.bound,</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Child</span> <span class="keyword">extends</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">    <span class="comment">// THROWS: TypeError: Cannot redefine property: arrowAction</span></span><br><span class="line">    arrowAction = <span class="function"><span class="params">()</span> =&gt;</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// OK</span></span><br><span class="line">    action() &#123;&#125;</span><br><span class="line">    boundAction() &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        <span class="keyword">super</span>()</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            arrowAction: override,</span><br><span class="line">            action: override,</span><br><span class="line">            boundAction: override,</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>想要将单个的 <em>action</em> <strong>绑定</strong> 到 <code>this</code>，可以使用 <code>action.bound</code> 代替箭头函数。<br>查看 <a href="https://zh.mobx.js.org/subclassing.html" target="_blank" rel="noopener"><strong>subclassing</strong></a> 获取更多信息。</p>
<h4 id="异步-actions"><a href="#异步-actions" class="headerlink" title="异步 actions"></a>异步 actions</h4><p>从本质上讲，异步进程在 MobX 中不需要任何特殊处理，因为不论是何时引发的所有 reactions 都将会自动更新。 而且因为可观察对象是可变的，因此在 action 执行过程中保持对它们的引用一般是安全的。 然而，在异步进程中更新可观察对象的每个步骤（tick）都应该被标识为 <code>action</code>。 我们可以通过利用上述的 API 以多种方式实现这一点，如下所示。</p>
<p>例如，在处理 Promise 时，更新 state 的处理程序应该被 <code>action</code> 包装起来，或者被标记为 actions，如下所示。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Wrap handlers in `action`</span></span><br><span class="line"><span class="comment">/*Promise 的决议处理程序是我们以内联的方式处理的，但是会在一开始的 action 执行完成之后运行，因此需要使用 action 对它们进行包装：*/</span></span><br><span class="line"><span class="keyword">import</span> &#123; action, makeAutoObservable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    githubProjects = []</span><br><span class="line">    state = <span class="string">"pending"</span> <span class="comment">// "pending", "done" or "error"</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        fetchGithubProjectsSomehow().then(</span><br><span class="line">            action(<span class="string">"fetchSuccess"</span>, projects =&gt; &#123;</span><br><span class="line">                <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">                <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">            &#125;),</span><br><span class="line">            action(<span class="string">"fetchError"</span>, error =&gt; &#123;</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">            &#125;)</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*Handle updates in separate actions</span></span><br><span class="line"><span class="comment">如果 Promise 的处理函数是类的字段，它们将由 makeAutoObservable 自动包装为 action：</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeAutoObservable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    githubProjects = []</span><br><span class="line">    state = <span class="string">"pending"</span> <span class="comment">// "pending", "done" or "error"</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        fetchGithubProjectsSomehow().then(<span class="keyword">this</span>.projectsFetchSuccess, <span class="keyword">this</span>.projectsFetchFailure)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    projectsFetchSuccess = <span class="function"><span class="params">projects</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    projectsFetchFailure = <span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">async/await + runInAction</span></span><br><span class="line"><span class="comment">await 之后的任何操作都不与其同在一个 tick 中，因此它们需要使用 action 包装。 在这里，我们可以利用 runInAction：</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">import</span> &#123; runInAction, makeAutoObservable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    githubProjects = []</span><br><span class="line">    state = <span class="string">"pending"</span> <span class="comment">// "pending", "done" or "error"</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">async</span> fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> projects = <span class="keyword">await</span> fetchGithubProjectsSomehow()</span><br><span class="line">            <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">            runInAction(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">            &#125;)</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">            runInAction(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*`flow` + generator function</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">import</span> &#123; flow, makeAutoObservable, flowResult &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    githubProjects = []</span><br><span class="line">    state = <span class="string">"pending"</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            fetchProjects: flow</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意星号, 这是一个 generator 函数!</span></span><br><span class="line">    *fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// Yield 代替 await.</span></span><br><span class="line">            <span class="keyword">const</span> projects = <span class="keyword">yield</span> fetchGithubProjectsSomehow()</span><br><span class="line">            <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">            <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">            <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">        &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">            <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> store = <span class="keyword">new</span> Store()</span><br><span class="line"><span class="keyword">const</span> projects = <span class="keyword">await</span> flowResult(store.fetchProjects())</span><br></pre></td></tr></table></figure>



<h4 id="使用-flow-代替-async-await-🚀"><a href="#使用-flow-代替-async-await-🚀" class="headerlink" title="使用 flow 代替 async / await {🚀}"></a>使用 flow 代替 async / await {🚀}</h4><p>用法：</p>
<ul>
<li><code>flow</code> <em>（注解）</em></li>
<li><code>flow(function* (args) { })</code></li>
</ul>
<p><code>flow</code> 包装器是一个可选的 <code>async</code> / <code>await</code> 替代方案，它让 MobX action 使用起来更加容易。</p>
<p><code>flow</code> 将一个 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator" target="_blank" rel="noopener">generator 函数</a> 作为唯一输入。 在 generator 内部，你可以使用 yield 串联 Promise（使用 <code>yield somePromise</code> 代替 <code>await somePromise</code>）。 flow 机制将会确保 generator 在 Promise resolve 之后继续运行或者抛出错误。</p>
<p>所以 <code>flow</code> 是 <code>async</code> / <code>await</code> 的一个替代方案，不需要再用 <code>action</code> 进行包装。它可以按照下面的方式使用：</p>
<ol>
<li>使用 <code>flow</code> 包装你的异步函数。</li>
<li>使用 <code>function *</code> 代替 <code>async</code>。</li>
<li>使用 <code>yield</code> 代替 <code>await</code>。</li>
</ol>
<p>以上 <a href="https://zh.mobx.js.org/actions.html#%E5%BC%82%E6%AD%A5-actions" target="_blank" rel="noopener"><code>flow</code> + generator function</a> 的示例展示了实际情况中的用法。</p>
<p>注意，使用 TypeScript 时才会需要 <code>flowResult</code> 函数。 它会因为使用 <code>flow</code> 装饰了一个方法而把返回的 generator 包裹在 Promise 中。 然而，TypeScript 并不会意识到这种转换，因此 <code>flowResult</code> 会确保 TypeScript 意识到这种类型的改变。</p>
<p><code>makeAutoObservable</code> 和它的小伙伴们会把 generators 自动推断成 <code>flow</code>。带有 <code>flow</code> 注解的成员是不可枚举的。</p>
<p><strong><code>flow.bound</code></strong></p>
<p>用法：</p>
<ul>
<li><code>flow.bound</code> <em>（注解）</em></li>
</ul>
<p><code>flow.bound</code> 注解可用于将方法自动绑定到正确的实例，这样 <code>this</code> 会始终被正确绑定在函数内部。 与 actions 一样，flows 默认可以使用 <a href="https://zh.mobx.js.org/actions.html#auto-bind" target="_blank" rel="noopener"><code>autoBind</code> 选项</a>。</p>
<p><strong>取消 flows {🚀}</strong></p>
<p>flow 的另一个好处就是它可以被取消。 <code>flow</code> 的返回值是一个 Promise，在 generator 函数运行完成时它将会被 resolve。 返回的 Promise 中还有一个 <code>cancel()</code> 方法，该方法可以打断正在运行的 generator 并取消它。 所有 <code>try</code> / <code>finally</code> 语句仍然会被运行。</p>
<p><strong>禁用强制性 action {🚀}</strong></p>
<p>默认情况下，MobX 6 和更高版本会要求您使用 action 来更改 state。 然而，你可以配置 MobX 来禁用这个行为。查看 <a href="https://zh.mobx.js.org/configuration.html#enforceactions" target="_blank" rel="noopener"><code>enforceActions</code></a>。 例如，这在单元测试场景中非常有用，因为警告并不总是很有价值。</p>
<h2 id="通过-computeds-派生信息"><a href="#通过-computeds-派生信息" class="headerlink" title="通过 computeds 派生信息"></a>通过 computeds 派生信息</h2><p>使用：</p>
<ul>
<li><code>computed</code> <em>（注解）</em></li>
<li><code>computed(options)</code> <em>（注解）</em></li>
<li><code>computed(fn, options?)</code></li>
</ul>
<p>计算值可以用来从其他可观察对象中派生信息。 计算值采用惰性求值，会缓存其输出，并且只有当其依赖的可观察对象被改变时才会重新计算。 它们在不被任何值观察时会被暂时停用。</p>
<p>从概念上讲，它们和电子表格中的公式非常相似，并且作用强大、不可被低估。它们可以协助减少你需要存储的状态的数量，并且是被高度优化过的。请尽可能使用它们。</p>
<p><strong>例子</strong></p>
<p>计算值可以通过在 JavaScript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get" target="_blank" rel="noopener">getters</a> 上添加 <code>computed</code> 注解来创建。 使用 <code>makeObservable</code> 将 getter 声明为 computed。或者如果你希望所有的 getters 被自动声明为 <code>computed</code>，可以使用 <code>makeAutoObservable</code>，<code>observable</code> 或者 <code>extendObservable</code>。</p>
<p>下面的示例依靠 <a href="https://zh.mobx.js.org/reactions.html" target="_blank" rel="noopener">Reactions {🚀}</a> 高级部分中的 <a href="https://zh.mobx.js.org/reactions.html#autorun" target="_blank" rel="noopener"><code>autorun</code></a> 来辅助说明计算值的意义。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; makeObservable, observable, computed, autorun &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OrderLine</span> </span>&#123;</span><br><span class="line">    price = <span class="number">0</span></span><br><span class="line">    amount = <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(price) &#123;</span><br><span class="line">        makeObservable(<span class="keyword">this</span>, &#123;</span><br><span class="line">            price: observable,</span><br><span class="line">            amount: observable,</span><br><span class="line">            total: computed</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">this</span>.price = price</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">get</span> total() &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"Computing..."</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.price * <span class="keyword">this</span>.amount</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> order = <span class="keyword">new</span> OrderLine(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> stop = autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Total: "</span> + order.total)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// Computing...</span></span><br><span class="line"><span class="comment">// Total: 0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(order.total)</span><br><span class="line"><span class="comment">// (不会重新计算!)</span></span><br><span class="line"><span class="comment">// 0</span></span><br><span class="line"></span><br><span class="line">order.amount = <span class="number">5</span></span><br><span class="line"><span class="comment">// Computing...</span></span><br><span class="line"><span class="comment">// (无需 autorun)</span></span><br><span class="line"></span><br><span class="line">order.price = <span class="number">2</span></span><br><span class="line"><span class="comment">// Computing...</span></span><br><span class="line"><span class="comment">// Total: 10</span></span><br><span class="line"></span><br><span class="line">stop()</span><br><span class="line"></span><br><span class="line">order.price = <span class="number">3</span></span><br><span class="line"><span class="comment">// 计算值和 autorun 都不会被重新计算.</span></span><br></pre></td></tr></table></figure>

<p>上面的例子很好地展示了 <code>计算值</code> 的好处，它充当了缓存点的角色。 即使我们改变了 <code>amount</code>，进而触发了 <code>total</code> 的重新计算， 也不会触发 <code>autorun</code>，因为 <code>total</code> 将会检测到其输出未发生任何改变，所以也不需要更新 <code>autorun</code>。</p>
<p>相比之下，如果 <code>total</code> 没有被注解，那么 <code>autorun</code> 会把副作用运行 3 次， 因为它将直接依赖于 <code>total</code> 和 <code>amount</code>。<a href="https://codesandbox.io/s/computed-3cjo9?file=/src/index.tsx" target="_blank" rel="noopener">自己试一下吧</a>。</p>
<p><img src="/../../image/Mobx-React/computed-example.png" alt="computed graph"></p>
<p>上图是为以上示例创建的依赖图。</p>
<p><strong>规则</strong></p>
<p>使用计算值时，请遵循下面的最佳实践：</p>
<ol>
<li>它们不应该有副作用或者更新其他可观察对象。</li>
<li>避免创建和返回新的可观察对象。</li>
</ol>
<h3 id="Options-🚀-1"><a href="#Options-🚀-1" class="headerlink" title="Options {🚀}"></a>Options {🚀}</h3><p>通常情况下，<code>computed</code> 是可以开箱即用的，但是可以通过传入 <code>options</code> 参数自定义其行为。</p>
<p><strong><code>name</code></strong></p>
<p>该字符串在 <a href="https://zh.mobx.js.org/analyzing-reactivity.html#spy" target="_blank" rel="noopener">Spy event listeners</a> 和 <a href="https://github.com/mobxjs/mobx-devtools" target="_blank" rel="noopener">MobX developer tools</a> 中用作调试名称。</p>
<p><strong><code>equals</code></strong></p>
<p>默认设置为 <code>comparer.default</code>。它充当一个比较函数，用于比较上一个值和下一个值。如果该函数认为两个值相等，那么观察者们将不会被重新计算。</p>
<p>在处理其他库的结构性数据和类型时，这个选项会很有用。例如，<code>(a, b) =&gt; a.isSame(b)</code>可以被用在一个 <a href="https://momentjs.com/" target="_blank" rel="noopener">moment</a> 计算实例上。如果你想使用结构比较或者浅比较来确定新值是否与之前的值不同， <code>comparer.structural</code> 和 <code>comparer.shallow</code> 就会派上用场，最后还会通知观察者。</p>
<p>查看上面的 <a href="https://zh.mobx.js.org/computeds.html#computed-struct" target="_blank" rel="noopener"><code>computed.struct</code></a> 部分。</p>
<p><strong>内置 compares</strong></p>
<p>MobX 提供了四种内置的 <code>comparer</code> 方法，这些方法满足 <code>computed</code> 的 <code>equals</code> 选项的大多数需求：</p>
<ul>
<li><code>comparer.identity</code> 使用全等 （<code>===</code>）运算符确定两个值是否相同。</li>
<li><code>comparer.default</code> 与 <code>comparer.identity</code> 相同，但是其认为 <code>NaN</code> 等于 <code>NaN</code>。</li>
<li><code>comparer.structural</code> 执行深层的结构比较以确定两个值是否相同。</li>
<li><code>comparer.shallow</code> 执行浅层的结构比较以确定两个值是否相同。</li>
</ul>
<p>你可以从 <code>MobX</code> 导入 <code>comparer</code> 来访问这些方法。它们也可以用于 <code>reaction</code>。</p>
<p><strong><code>requiresReaction</code></strong></p>
<p>推荐在非常昂贵的计算值中将这个选项设置为 <code>true</code>。如果你试图在响应式上下文之外读取这样的计算值——这种情况下，它可能不会被缓存起来——就会导致计算值抛出错误，而不是进行昂贵的重新计算。</p>
<p><strong><code>keepAlive</code></strong></p>
<p>这个选项会避免计算值在未被观察时被暂时停用（可以查看上面的解释）。这可能会导致内存泄漏，这种内存泄漏与我们在 <a href="https://zh.mobx.js.org/reactions.html#always-dispose-of-reactions" target="_blank" rel="noopener">reactions</a> 中讨论到的类似。</p>
<h2 id="使用-reactions-处理副作用"><a href="#使用-reactions-处理副作用" class="headerlink" title="使用 reactions 处理副作用"></a>使用 reactions 处理副作用</h2><p>reactions 是需要理解的重要概念，因为他可以将 MobX 中所有的特性有机地融合在一起。 reactions 的目的是对自动发生的副作用进行建模。 它们的意义在于为你的可观察状态创建消费者，以及每当<em>关联</em>的值发生变化时，<em>自动</em>运行副作用。</p>
<p>然而，理解了这一点之后，重要的是要认识到这里所讨论的 API 应该很少会被用到。 它们经常被抽象到其他的库里面（例如，mobx-react）或者你的应用程序中其他特定的抽象库。</p>
<p>但是，为了理解 MobX，让我们看一下如何创建 reactions。 最简单的方式是使用 <a href="https://zh.mobx.js.org/reactions.html#autorun" target="_blank" rel="noopener"><code>autorun</code></a> 工具函数。 除此之外，还有 <a href="https://zh.mobx.js.org/reactions.html#reaction" target="_blank" rel="noopener"><code>reaction</code></a> 和 <a href="https://zh.mobx.js.org/reactions.html#when" target="_blank" rel="noopener"><code>when</code></a>。</p>
<p><strong>Autorun</strong></p>
<p>用法：</p>
<ul>
<li><code>autorun(effect: (reaction) =&gt; void)</code></li>
</ul>
<p><code>autorun</code> 函数接受一个函数作为参数，每当该函数所观察的值发生变化时，它都应该运行。 当你自己创建 <code>autorun</code> 时，它也会运行一次。它仅仅对可观察状态的变化做出响应，比如那些你用 <code>observable</code> 或者 <code>computed</code> 注释的。</p>
<p><strong>tracking 如何工作</strong></p>
<p>Autorun 通过在<em>响应式上下文</em>运行 <code>effect</code> 来工作。在给定的函数执行期间，MobX 会持续跟踪被 effect 直接或间接<em>读取</em>过的所有可观察对象和计算值。 一旦函数执行完毕，MobX 将收集并订阅所有被读取过的可观察对象，并等待其中任意一个再次发生改变。 一旦有改变发生，<code>autorun</code> 将会再次触发，重复整个过程。</p>
<p><img src="/../../image/Mobx-React/autorun.png" alt="autorun"></p>
<p>这就是下面的示例的工作方式。</p>
<p><strong>例子</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; makeAutoObservable, autorun &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Animal</span> </span>&#123;</span><br><span class="line">    name</span><br><span class="line">    energyLevel</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(name) &#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name</span><br><span class="line">        <span class="keyword">this</span>.energyLevel = <span class="number">100</span></span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    reduceEnergy() &#123;</span><br><span class="line">        <span class="keyword">this</span>.energyLevel -= <span class="number">10</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">get</span> isHungry() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.energyLevel &lt; <span class="number">50</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> giraffe = <span class="keyword">new</span> Animal(<span class="string">"Gary"</span>)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Energy level:"</span>, giraffe.energyLevel)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (giraffe.isHungry) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"Now I'm hungry!"</span>)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"I'm not hungry!"</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"Now let's change state!"</span>)</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    giraffe.reduceEnergy()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行上面的代码，你将会看到下面的输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">Energy level: 100</span><br><span class="line">I&#39;m not hungry!</span><br><span class="line">Now let&#39;s change state!</span><br><span class="line">Energy level: 90</span><br><span class="line">Energy level: 80</span><br><span class="line">Energy level: 70</span><br><span class="line">Energy level: 60</span><br><span class="line">Energy level: 50</span><br><span class="line">Energy level: 40</span><br><span class="line">Now I&#39;m hungry!</span><br><span class="line">Energy level: 30</span><br><span class="line">Energy level: 20</span><br><span class="line">Energy level: 10</span><br><span class="line">Energy level: 0</span><br></pre></td></tr></table></figure>

<p>正如你在上面输出的前两行看到的，两个 <code>autorun</code> 函数在初始化时都会运行一次。这就是在运行 <code>for</code> 循环前可以看到的内容。</p>
<p>一旦我们运行 <code>for</code> 循环使用 <code>reduceEnergy</code> action 改变 <code>energyLevel</code>， 每当 <code>autorun</code> 观察到可观察状态的变化时， 我们将会看到一条新的 log 条目被打印出来：</p>
<ol>
<li>对于<em>“Energy level”</em>函数，它总是可以检测到 <code>energyLevel</code> 可观察对象的变化，总共发生 10 次。</li>
<li>对于<em>“Now I’m hungry”</em>函数，它总是可以检测到 <code>isHungry</code> 计算值的变化， 总共发生 1 次。</li>
</ol>
<p><strong>Reaction</strong></p>
<p>用法：</p>
<ul>
<li><code>reaction(() =&gt; value, (value, previousValue, reaction) =&gt; { sideEffect }, options?)</code>.</li>
</ul>
<p><code>reaction</code> 类似于 <code>autorun</code>，但可以让你更加精细地控制要跟踪的可观察对象。 它接受两个函数作为参数：第一个，<em>data</em> 函数，其是被跟踪的函数并且其返回值将会作为第二个函数，<em>effect</em> 函数，的输入。 重要的是要注意，副作用<em>只会</em>对 data 函数中<em>被访问过</em>的数据做出反应，这些数据可能少于 effect 函数中实际使用的数据。</p>
<p>一般的模式是在 <em>data</em> 函数中返回你在副作用中需要的所有数据， 并以这种方式更精确地控制副作用触发的时机。 与 <code>autorun</code> 不同，副作用在初始化时不会自动运行，而只会在 data 表达式首次返回新值之后运行。</p>
<p><strong>When</strong></p>
<p>使用：</p>
<ul>
<li><code>when(predicate: () =&gt; boolean, effect?: () =&gt; void, options?)</code></li>
<li><code>when(predicate: () =&gt; boolean, options?): Promise</code></li>
</ul>
<p><code>when</code> 会观察并运行给定的 <em>predicate</em> 函数，直到其返回 <code>true</code>。 一旦 predicate 返回了 true，给定的 <em>effect</em> 函数就会执行并且自动执行器函数将会被清理掉。</p>
<p>如果你没有传入 <code>effect</code> 函数，<code>when</code> 函数返回一个 <code>Promise</code> 类型的 disposer，并允许你手动取消。</p>
<p><strong><code>await when(...)</code></strong></p>
<p>如果你没有提供 <code>effect</code> 函数，<code>when</code> 将会返回一个 <code>Promise</code>。这样会跟 <code>async / await</code> 很好地结合在一起，让你可以等待可观察对象中的变化。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">await</span> when(<span class="function"><span class="params">()</span> =&gt;</span> that.isVisible)</span><br><span class="line">    <span class="comment">// etc...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果要提前取消 <code>when</code>，可以对它返回的 Promise 调用 <code>.cancel()</code> 函数。</p>
<p><strong>规则</strong></p>
<p>这里是一些 reactive context 需要遵守的规则：</p>
<ol>
<li>默认情况下，如果可观察对象发生了改变，受其影响的 reactions 会立即（同步）运行。然而，它们直到当前最外层的 (trans)action 执行结束后才会运行。</li>
<li>autorun 只会跟踪给定函数在同步执行过程中所读取的可观察对象，不会跟踪异步发生的变化。</li>
<li>autorun 不会跟踪被其调用的 action 所读取的可观察对象，因为 action 始终<em>不会被追踪</em>。</li>
</ol>
<p>有关 MobX 会与不会对各种值作出响应的更多示例，请查看 <a href="https://zh.mobx.js.org/understanding-reactivity.html" target="_blank" rel="noopener">理解响应性</a> 部分。 对于依赖跟踪如何工作的更详细的技术细节，请阅读博客 <a href="https://hackernoon.com/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254" target="_blank" rel="noopener">Becoming fully reactive: an in-depth explanation of MobX</a>。</p>
<h3 id="Always-dispose-of-reactions"><a href="#Always-dispose-of-reactions" class="headerlink" title="Always dispose of reactions"></a>Always dispose of reactions</h3><p>传递给 <code>autorun</code>，<code>reaction</code> 和 <code>when</code> 的函数只有在它们观察的所有对象都被 GC 之后才会被 GC。原则上，它们一直等待可观察对象发生新的变化。 为了阻止 reactions 永远地等待下去，它们总是会返回一个 disposer 函数，该函数可以用来停止执行并且取消订阅所使用的任何可观察对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> counter = observable(&#123; <span class="attr">count</span>: <span class="number">0</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化一个 autorun 并且打印 0.</span></span><br><span class="line"><span class="keyword">const</span> disposer = autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(counter.count)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 打印: 1</span></span><br><span class="line">counter.count++</span><br><span class="line"></span><br><span class="line"><span class="comment">// 停止 autorun.</span></span><br><span class="line">disposer()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不会打印消息.</span></span><br><span class="line">counter.count++</span><br></pre></td></tr></table></figure>

<p>我们强烈建议你，一旦不再需要这些方法中的副作用时，请务必调用它们所返回的 disposer 函数。 否则可能导致内存泄漏。</p>
<p><code>reaction</code> 和 <code>autorun</code> 中 effect 函数的第二个参数 <code>reaction</code> 也可以被用来提前把 reaction 清理掉（通过调用 <code>reaction.dispose()</code>）。</p>
<h3 id="谨慎地使用-reactions！"><a href="#谨慎地使用-reactions！" class="headerlink" title="谨慎地使用 reactions！"></a>谨慎地使用 reactions！</h3><p>就像上面已经说过的那样，你不会经常创建 reactions。 很有可能你的应用程序不会直接使用这些 API 中的任何一个，而只会通过比如使用 mobx-react 绑定中的 <code>observer</code> 这样间接的方式创建出 reaction。</p>
<p>在你创建 reaction 之前，最好检查一下它是否符合下面几条原则：</p>
<ol>
<li><strong>只有在引起副作用的一方与副作用之间没有直接关系的情况下才使用 reaction</strong>： 如果一个副作用会为了响应很小的一组 events 或 actions 而执行，那么直接从那些特定的 action 中触发这个副作用通常会更容易理解。例如，如果按下表单提交按钮会导致一个 POST 网络请求的发送，那么为了响应 <code>onclick</code> 事件，直接触发这个副作用就会比通过 reaction 间接触发更容易理解。相比之下，如果你对表单状态的一切修改最后都会被自动存储到 localStorage，那么使用一个 reaction 可能就会很有帮助，这样你就不用在每个独立的 <code>onChange</code> 事件中触发这个副作用了。</li>
<li><strong>reactions 不应该更新其他可观察对象</strong>：这个 reaction 是否会修改其他可观察对象？如果答案是肯定的，那么你一般应该把你想要更新的可观察对象注解为 <a href="https://zh.mobx.js.org/computeds.html" target="_blank" rel="noopener"><code>computed</code></a> 值。例如，如果一个待办事项的集合 <code>todos</code> 发生了变化，那么请不要使用 reaction 来计算剩余待办 <code>remainingTodos</code> 的数量，而要把 <code>remainingTodos</code> 注解为计算值。这将使得代码更容易理解和调试。reaction 不应该计算生成新的数据，而只应该触发副作用。</li>
<li><strong>reactions 应该是独立的</strong>：你的代码是否依赖其他必须首先运行的 reaction？如果发生这种情况，你可能违反了第一条规则， 你可以选择将你需要创建的新 reaction 合并到它所依赖 reaction 中。MobX 并不能保证 reaction 的执行顺序。</li>
</ol>
<p>有些实践并不符合上述原则。这就是为什么它们是<em>原则</em>，而不是<em>法则</em>。 但是，例外情况很少见，只有在万不得已的情况下才违反它们。</p>
<h3 id="Options-🚀-2"><a href="#Options-🚀-2" class="headerlink" title="Options {🚀}"></a>Options {🚀}</h3><p>上面提到的 <code>autorun</code>，<code>reaction</code> 和 <code>when</code> 都可以通过传递一个 <code>options</code> 参数来进一步自定义它们的行为。</p>
<p><strong><code>name</code></strong></p>
<p>该字符串在 <a href="https://zh.mobx.js.org/analyzing-reactivity.html#spy" target="_blank" rel="noopener">Spy event listeners</a> 和 <a href="https://github.com/mobxjs/mobx-devtools" target="_blank" rel="noopener">MobX developer tools</a> 中用作此 reaction 的调试名称。</p>
<p><strong><code>fireImmediately</code> <em>(reaction)</em></strong></p>
<p>布尔值，指示在第一次运行 <em>data</em> 函数后立即触发 <em>effect</em> 函数。默认为 <code>false</code>。</p>
<p><strong><code>delay</code> <em>(autorun, reaction)</em></strong></p>
<p>对 effect 函数进行节流的毫秒数。如果是 0（默认值），将不进行节流操作。</p>
<p><strong><code>timeout</code> <em>(when)</em></strong></p>
<p>设置一段有限的 <code>when</code> 将要等待的时间。如果过了时限，<code>when</code> 将会 reject 或抛出错误。</p>
<p><strong><code>onError</code></strong></p>
<p>默认情况下，将记录 reaction 中引发的任何异常，但不会向上抛出。这样确保了在一个 reaction 中引发异常将不会影响其他按计划执行的不相关的 reaction 的调度。这同样允许 reaction 从异常中恢复。抛出异常不会打断 MobX 所做的依赖跟踪，所以如果消除了引发异常的因素，则后续的 reaction 可能会再次正常完成。此选项允许覆盖该行为。可以使用 <a href="https://zh.mobx.js.org/configuration.html#disableerrorboundaries-boolean" target="_blank" rel="noopener">configure</a> 设置全局错误处理程序或完全禁用错误捕获。</p>
<p><strong><code>scheduler</code> <em>(autorun, reaction)</em></strong></p>
<p>设置一个自定义调度程序，用来决定应该如何调度 autorun 函数的重新运行。它接受一个函数作为参数，这个函数应该在将来某个时刻被调用，例如：<code>{ scheduler: run =&gt; { setTimeout(run, 1000) }}</code>。</p>
<p><strong><code>equals</code>: (reaction)</strong></p>
<p>默认情况下设置为 <code>comparer.default</code>。如果指定，则此比较函数用于比较 <em>data</em> 函数产生的上一个值和下一个值。当且仅当此函数返回 false 时，<em>effect</em> 函数才会被执行。</p>
<h1 id="Mobx-and-React"><a href="#Mobx-and-React" class="headerlink" title="Mobx and React"></a>Mobx and React</h1><h2 id="集成React（react-integration）"><a href="#集成React（react-integration）" class="headerlink" title="集成React（react-integration）"></a>集成React（react-integration）</h2><p>用法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; observer &#125; <span class="keyword">from</span> <span class="string">"mobx-react-lite"</span> <span class="comment">// Or "mobx-react".</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> MyComponent = observer(<span class="function"><span class="params">props</span> =&gt;</span> ReactElement)</span><br></pre></td></tr></table></figure>

<p>MobX 可以独立于 React 运行, 但是他们通常是结合在一起使用, 在 <a href="https://zh.mobx.js.org/the-gist-of-mobx.html" target="_blank" rel="noopener">Mobx的宗旨（The gist of MobX）</a> 一文中你会经常看见集成React最重要的一部分：用于包裹React Component的 <code>observer</code> <a href="https://reactjs.org/docs/higher-order-components.html" target="_blank" rel="noopener">HOC</a>方法。</p>
<p><code>observer</code> 是你可以自主选择的，<a href="https://zh.mobx.js.org/installation.html#installation" target="_blank" rel="noopener">在安装时（during installation）</a>独立提供的 React bindings 包。 在下面的例子中,我们将使用更加轻量的<a href="https://github.com/mobxjs/mobx-react-lite" target="_blank" rel="noopener"><code>mobx-react-lite</code> 包</a>。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">"react"</span></span><br><span class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">"react-dom"</span></span><br><span class="line"><span class="keyword">import</span> &#123; makeAutoObservable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"><span class="keyword">import</span> &#123; observer &#125; <span class="keyword">from</span> <span class="string">"mobx-react-lite"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Timer</span> </span>&#123;</span><br><span class="line">    secondsPassed = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    increaseTimer() &#123;</span><br><span class="line">        <span class="keyword">this</span>.secondsPassed += <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> myTimer = <span class="keyword">new</span> Timer()</span><br><span class="line"></span><br><span class="line"><span class="comment">//被`observer`包裹的函数式组件会被监听在它每一次调用前发生的任何变化</span></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function">(<span class="params">&#123; timer &#125;</span>) =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123;timer.secondsPassed&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span>)</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">TimerView</span> <span class="attr">timer</span>=<span class="string">&#123;myTimer&#125;</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br><span class="line"></span><br><span class="line">setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    myTimer.increaseTimer()</span><br><span class="line">&#125;, <span class="number">1000</span>)</span><br></pre></td></tr></table></figure>

<p><code>observer</code> HOC 将自动订阅 React components 中任何 <em>在渲染期间</em> 被使用的 <em>可被观察的对象</em> 。 因此, 当任何可被观察的对象 <em>变化</em> 发生时候 组件会自动进行重新渲染（re-render）。 它还会确保组件在 <em>没有变化</em> 发生的时候不会进行重新渲染（re-render）。 但是, 更改组件的可观察对象的不可读属性, 也不会触发重新渲染（re-render）。</p>
<p>在实际项目中，这一特性使得MobX应用程序能够很好的进行开箱即用的优化，并且通常不需要任何额外的代码来防止过度渲染。</p>
<p>要想让<code>observer</code>生效, 并不需要关心这些对象 <em>如何传递到</em> 组件的（它们只要能传递给组件即可 ·译者注）, 只需要关心他们是否是可读的。 深层嵌套的可观察对象也没有问题, 复杂的表达式类似 <code>todos[0].author.displayName</code> 也是可以使用的。 与其他必须显式声明或预先计算数据依赖关系的框架（例如 selectors）相比，这种发生的订阅机制就显得更加精确和高效。</p>
<h3 id="本地与外部状态（Local-and-external-state）"><a href="#本地与外部状态（Local-and-external-state）" class="headerlink" title="本地与外部状态（Local and external state）"></a>本地与外部状态（Local and external state）</h3><p>在 Mobx 可以非常灵活的组织或管理（state）, 从（技术角度讲）它不关心我们如何读取可观察对象，也不关心他们来自哪里。 下面的例子将通过不同的设计模式去使用被 <code>observer</code>包裹的组件。</p>
<p><strong><code>observer</code> 组件中使用外部状态 （Using external state in <code>observer</code> components）</strong>**</p>
<p>1、使用props属性</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//可被观察对象可以通过组件的props属性传入 (在下面的例子中):</span></span><br><span class="line"><span class="keyword">import</span> &#123; observer &#125; <span class="keyword">from</span> <span class="string">"mobx-react-lite"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> myTimer = <span class="keyword">new</span> Timer() <span class="comment">// See the Timer definition above.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function">(<span class="params">&#123; timer &#125;</span>) =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123;timer.secondsPassed&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过props传递myTimer.</span></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">TimerView</span> <span class="attr">timer</span>=<span class="string">&#123;myTimer&#125;</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p>2、使用全局变量</p>
<p>虽然我们不关心是 <em>如何</em> 引用（reference）的可观察对象,但是我们可以使用 （consume） 外部作用域（outer scopes directly）的可观察对象 (类似通过 import这样的方法, 等等)：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myTimer = <span class="keyword">new</span> Timer() <span class="comment">//  Timer 定义在上面.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 没有props, `myTimer` 立刻变成了闭包。</span></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function"><span class="params">()</span> =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123;myTimer.secondsPassed&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span>)</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">TimerView</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p>直接使用可观察对象效果很好，但是这通常会是通过模块引入，这种写法可能会使单元测试变得复杂。 因此，我们建议使用React Context。</p>
<p>3、使用<a href="https://reactjs.org/docs/context.html" target="_blank" rel="noopener">React Context</a>共享整个可观察子树是一种很不错的选择：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observer&#125; <span class="keyword">from</span> <span class="string">'mobx-react-lite'</span></span><br><span class="line"><span class="keyword">import</span> &#123;createContext, useContext&#125; <span class="keyword">from</span> <span class="string">"react"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TimerContext = createContext&lt;Timer&gt;()</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 从context中获取timer.</span></span><br><span class="line">    <span class="keyword">const</span> timer = useContext(TimerContext) <span class="comment">// 可以在上面查看 Timer的定义。</span></span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        &lt;span&gt;Seconds passed: &#123;timer.secondsPassed&#125;&lt;<span class="regexp">/span&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">&#125;)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">ReactDOM.render(</span></span><br><span class="line"><span class="regexp">    &lt;TimerContext.Provider value=&#123;new Timer()&#125;&gt;</span></span><br><span class="line"><span class="regexp">        &lt;TimerView /</span>&gt;</span><br><span class="line">    &lt;<span class="regexp">/TimerContext.Provider&gt;,</span></span><br><span class="line"><span class="regexp">    document.body</span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure>

<p>需要注意的是我们并不推荐每一个不同的 <code>值（value）</code> 都通过不同的 <code>Provider</code>来传递 . 在使用Mobx的过程中不需要这样做, 因为共享的可观察对象会更新他自己</p>
<p><strong>在<code>observer</code> 组件中使用全局可观察对象（Using local observable state in <code>observer</code> components）</strong></p>
<p>因为使用 <code>observer</code> 的可观察对象可以来自任何地方, 他们也可以使用local state（全局的state·译者注）。 再次声明，不同操作方式对于我们而言都是有价值的。</p>
<p>1、useState 和 observable class</p>
<p>使用全局可观察对象的最简单的方式就是通过<code>useState</code>去存储一个全局可观察对象的引用。 需要注意的是, 因为我们不需要替换全局可观察对象的引用,所以我们其实可以完全不声明<code>useState</code>的更新方法:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; observer &#125; <span class="keyword">from</span> <span class="string">"mobx-react-lite"</span></span><br><span class="line"><span class="keyword">import</span> &#123; useState &#125; <span class="keyword">from</span> <span class="string">"react"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> [timer] = useState(<span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">new</span> Timer()) <span class="comment">// Timer的定义在上面（正如上面所说的那样这里我们忽略了更新方法的定义·译者注）。</span></span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123;timer.secondsPassed&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">TimerView</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p>如果你想要类似我们官方的例子那样自动更新 timer , 使用<code>useEffect</code> 可能是 React 中比较典型的写法：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> handle = setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        timer.increaseTimer()</span><br><span class="line">    &#125;, <span class="number">1000</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        clearInterval(handle)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, [timer])</span><br></pre></td></tr></table></figure>

<p>2、useState 与全局可观察对象</p>
<p>如刚才说的那样, 直接创建一个可观察的对象，而不是使用classes（这里的类指的是全局定义的Mobx state，在它们是使用class声明的）。 我们可以参考 <a href="https://zh.mobx.js.org/observable-state.html#observable" target="_blank" rel="noopener">observable</a>这篇文章：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; observer &#125; <span class="keyword">from</span> <span class="string">"mobx-react-lite"</span></span><br><span class="line"><span class="keyword">import</span> &#123; observable &#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"><span class="keyword">import</span> &#123; useState &#125; <span class="keyword">from</span> <span class="string">"react"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> [timer] = useState(<span class="function"><span class="params">()</span> =&gt;</span></span><br><span class="line">        observable(&#123;</span><br><span class="line">            secondsPassed: <span class="number">0</span>,</span><br><span class="line">            increaseTimer() &#123;</span><br><span class="line">                <span class="keyword">this</span>.secondsPassed++</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line">    )</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123;timer.secondsPassed&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">TimerView</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p>3、useLocalObservable hook</p>
<p><code>const [store] = useState(() =&gt; observable({ /* something */}))</code> 是非常通用的一套写法， 为了简化这个写法我们可以调用<code>mobx-react-lite</code> 包中的 <a href="https://github.com/mobxjs/mobx-react#uselocalobservable-hook" target="_blank" rel="noopener"><code>useLocalObservable</code></a> hook ,可以将上面的例子简化成：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; observer, useLocalObservable &#125; <span class="keyword">from</span> <span class="string">"mobx-react-lite"</span></span><br><span class="line"><span class="keyword">import</span> &#123; useState &#125; <span class="keyword">from</span> <span class="string">"react"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> timer = useLocalObservable(<span class="function"><span class="params">()</span> =&gt;</span> (&#123;</span><br><span class="line">        secondsPassed: <span class="number">0</span>,</span><br><span class="line">        increaseTimer() &#123;</span><br><span class="line">            <span class="keyword">this</span>.secondsPassed++</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;))</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123;timer.secondsPassed&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">TimerView</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p><strong>你可能并不需要全局的可观察状态 （You might not need locally observable state）</strong></p>
<p>通常来讲，我们推荐在编写全局公用组件的时候不要立刻使用Mobx的可观察能力， 因为从技术角度来讲他可能会使你无法使用一些React 的 Suspense 的方法特性。 总的来说，使用Mobx的可观察能力会捕获组件间的域状态（domain data）可能会 (包含子组件的)。像是todo item, users, bookings, 等等（就是说最好不要用mobx共享一些组件内的状态·译者注）。</p>
<p>状态类似获取UI state, 类似加载的 state, 选择的 state,等等, 最好还是使用 <a href="https://reactjs.org/docs/hooks-state.html" target="_blank" rel="noopener"><code>useState</code> hook</a>, 这样可以让你使用高级的 React suspense特性。</p>
<p>使用Mobx的可观察能力作为 React components 的一种状态补充，比如出现以下情况： 1) 层级很深, 2) 拥有计算属性 3) 需要共享状态给其它 <code>observer</code> components。</p>
<p><strong>始终在<code>observer</code> 组件中使用可观察能力（Always read observables inside <code>observer</code> components）</strong></p>
<p>你可能会感到疑惑, 我应该什么时候使用 <code>observer</code>? 大体上说: _ <code>observer</code>应用于所有组件的可观察数据 _ 。</p>
<p><code>observer</code> 是使用修饰模式增强你的组件, 而不是它调用你的组件. 所以通常所有的组件都可能用了 <code>observer</code>，但是不要担心， 它不会导致性能损失。从另一个角度讲, 更多的 <code>observer</code> 组件可以使渲染更高效，因为它们更新数据的颗粒度更细。</p>
<h3 id="小贴士-尽可能晚地从对象中获取值"><a href="#小贴士-尽可能晚地从对象中获取值" class="headerlink" title="小贴士: 尽可能晚地从对象中获取值"></a>小贴士: 尽可能晚地从对象中获取值</h3><p>只要你传递引用，<code>observer</code> 就可以很好的工作。只要获取到内部的属性，基于 <code>observer</code> 的组件 就会渲染到 DOM / low-level components（DOM一般是浏览器环境，low-level components 一般是RN环境·译者注）。换句话说， <code>observer</code> 会根据实际情况响应你定义的对象中的值的’引用’。</p>
<p>下面的例子中, <code>TimerView</code> 组件<strong>不会</strong>响应未来的更新，因为<code>.secondsPassed</code>不是在 <code>observer</code>组件内部读取的而是在外部读取的,因此它<em>不会</em>被追踪到：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> TimerView = observer(<span class="function">(<span class="params">&#123; secondsPassed &#125;</span>) =&gt;</span> <span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123;secondsPassed&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span>)</span><br><span class="line"></span><br><span class="line">React.render(<span class="xml"><span class="tag">&lt;<span class="name">TimerView</span> <span class="attr">secondsPassed</span>=<span class="string">&#123;myTimer.secondsPassed&#125;</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p>需要注意的一点是它不同于其它的观念模式库像是 <code>react-redux</code>那样, redux 中强调尽可能早的获取和传递原始值以获得更好的副作用响应。 如果你还是没有理解, 可以先阅读 <a href="https://zh.mobx.js.org/understanding-reactivity.html" target="_blank" rel="noopener">理解响应式（Understanding reactivity）</a> 这篇文章。</p>
<h3 id="不要将可观察对象传递到-不是observer的组件中（Don’t-pass-observables-into-components-that-aren’t-observer）"><a href="#不要将可观察对象传递到-不是observer的组件中（Don’t-pass-observables-into-components-that-aren’t-observer）" class="headerlink" title="不要将可观察对象传递到 不是observer的组件中（Don’t pass observables into components that aren’t observer）"></a>不要将可观察对象传递到 不是<code>observer</code>的组件中（Don’t pass observables into components that aren’t <code>observer</code>）</h3><p>通过<code>observer</code>包裹的组件 <em>只可以</em> 订阅到在 <em>他们自己</em> 渲染的期间的可观察对象. 如果要将可观察对象 objects / arrays / maps 传递到子组件中, 他们必须被 <code>observer</code> 包裹。 通过callback回调的组件也是一样。</p>
<p>如果你非要传递可观察对象到未被<code>observer</code>包裹的组件中， 要么是因为它是第三方组件，要么你需要组件对Mobx无感知，那你必须在传递前 <a href="https://zh.mobx.js.org/observable-state.html#%E5%B0%86-observable-%E8%BD%AC%E6%8D%A2%E5%9B%9E%E6%99%AE%E9%80%9A%E7%9A%84-javascript-%E9%9B%86%E5%90%88" target="_blank" rel="noopener">转换可观察对象为显式 （convert the observables to plain JavaScript values or structures）</a> 。</p>
<p>关于上述的详细描述, 可以看一下下面的使用 <code>todo</code> 对象的例子， 一个 <code>TodoView</code> (observer)组件和一个虚构的接收一组对象映射入参的不是<code>observer</code>的<code>GridRow</code>组件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo</span> </span>&#123;</span><br><span class="line">    title = <span class="string">"test"</span></span><br><span class="line">    done = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        makeAutoObservable(<span class="keyword">this</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TodoView = observer(<span class="function">(<span class="params">&#123; todo &#125;: &#123; todo: Todo &#125;</span>) =&gt;</span></span><br><span class="line">   <span class="comment">// 错误: GridRow 不能获取到 todo.title/ todo.done 的变更</span></span><br><span class="line">   <span class="comment">//       因为他不是一个观察者（observer。</span></span><br><span class="line">   <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">GridRow</span> <span class="attr">data</span>=<span class="string">&#123;todo&#125;</span> /&gt;</span></span></span><br><span class="line"></span><br><span class="line">   <span class="comment">// 正确:在 `TodoView` 中显式的声明相关的`todo` ，</span></span><br><span class="line">   <span class="comment">//      到data中。</span></span><br><span class="line">   <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">GridRow</span> <span class="attr">data</span>=<span class="string">&#123;&#123;</span></span></span></span><br><span class="line"><span class="xml">       title: todo.title,</span></span><br><span class="line"><span class="xml">       done: todo.done</span></span><br><span class="line"><span class="xml">   &#125;&#125; /&gt;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">// 正确: 使用 `toJS`也是可以的, 并且是更清晰直白的方式。</span></span><br><span class="line">   <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">GridRow</span> <span class="attr">data</span>=<span class="string">&#123;toJS(todo)&#125;</span> /&gt;</span></span></span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<h3 id="回调组件可能会需要-lt-Observer-gt-（-Callback-components-might-require-lt-Observer-gt-）"><a href="#回调组件可能会需要-lt-Observer-gt-（-Callback-components-might-require-lt-Observer-gt-）" class="headerlink" title="回调组件可能会需要&lt;Observer&gt;（ Callback components might require &lt;Observer&gt;）"></a>回调组件可能会需要<code>&lt;Observer&gt;</code>（ Callback components might require <code>&lt;Observer&gt;</code>）</h3><p>想象一下在同样的例子中, <code>GridRow</code> 携带一个 <code>onRender</code>回调函数。 <code>onRender</code> 是 <code>GridRow</code>渲染生命周期的一部分, 而不是 <code>TodoView</code> 的render (甚至在语法层面都能看出来)，我们不得不保证回调组件是一个 <code>observer</code> 组件。 或者，我们可以使用 <a href="https://github.com/mobxjs/mobx-react#observer" target="_blank" rel="noopener">``</a>创建一个匿名观察者：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> TodoView = observer(<span class="function">(<span class="params">&#123; todo &#125;: &#123; todo: Todo &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 错误: GridRow.onRender 不能获得 todo.title / todo.done 中的改变</span></span><br><span class="line">    <span class="comment">//        因为它不是一个观察者（observer） 。</span></span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">GridRow</span> <span class="attr">onRender</span>=<span class="string">&#123;()</span> =&gt;</span> <span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;todo.title&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span>&#125; /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 正确: 将回调组件通过Observer包裹将会正确的获得变化。</span></span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">GridRow</span> <span class="attr">onRender</span>=<span class="string">&#123;()</span> =&gt;</span> <span class="tag">&lt;<span class="name">Observer</span>&gt;</span>&#123;() =&gt; <span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;todo.title&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span>&#125;<span class="tag">&lt;/<span class="name">Observer</span>&gt;</span>&#125; /&gt;</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

    </div>
    
    
    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
        <div class="reward-container">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作！</div>
  <button onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
      
      <div style="display: inline-block;">
        <img src="/images/alipay.png" alt="Li Yudong 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>

        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>Li Yudong
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx-React/" title="Mobx-React">http://example.com/2021/11/30/盒马技术栈初学/Mobx-React/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>

    
      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/%E5%85%AC%E5%8F%B8%E6%8A%80%E6%9C%AF%E6%A0%88/" rel="tag"># 公司技术栈</a>
          </div>
    
        

    
        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx%E8%BF%9B%E9%98%B6/" rel="prev" title="Mobx进阶">
      <i class="fa fa-chevron-left"></i> Mobx进阶
    </a></div>
      <div class="post-nav-item">
    <a href="/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx%E5%85%A5%E9%97%A8/" rel="next" title="Mobx入门">
      Mobx入门 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#前言"><span class="nav-number">1.</span> <span class="nav-text">前言</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#一个简单的例子"><span class="nav-number">1.1.</span> <span class="nav-text">一个简单的例子</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MobX-主旨"><span class="nav-number">1.2.</span> <span class="nav-text">MobX 主旨</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-定义-State-并使其可观察"><span class="nav-number">1.2.1.</span> <span class="nav-text">1. 定义 State 并使其可观察</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-使用-Action-更新-State"><span class="nav-number">1.2.2.</span> <span class="nav-text">2. 使用 Action 更新 State</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-创建-Derivations-以便自动对-State-变化进行响应"><span class="nav-number">1.2.3.</span> <span class="nav-text">3. 创建 Derivations 以便自动对 State 变化进行响应</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#3-1-通过-computed-对派生值进行建模"><span class="nav-number">1.2.3.1.</span> <span class="nav-text">3.1. 通过 computed 对派生值进行建模</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-2-使用-reaction-对副作用建模"><span class="nav-number">1.2.3.2.</span> <span class="nav-text">3.2. 使用 reaction 对副作用建模</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-3-响应式-React-组件"><span class="nav-number">1.2.3.3.</span> <span class="nav-text">3.3. 响应式 React 组件</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-4-自定义-Reaction"><span class="nav-number">1.2.3.4.</span> <span class="nav-text">3.4. 自定义 Reaction</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#原则"><span class="nav-number">1.2.4.</span> <span class="nav-text">原则</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Mobx-Core"><span class="nav-number">2.</span> <span class="nav-text">Mobx Core</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#创建可观察状态"><span class="nav-number">2.1.</span> <span class="nav-text">创建可观察状态</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#可用的注解"><span class="nav-number">2.1.1.</span> <span class="nav-text">可用的注解</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#局限性"><span class="nav-number">2.1.2.</span> <span class="nav-text">局限性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Options-🚀"><span class="nav-number">2.1.3.</span> <span class="nav-text">Options {🚀}</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#使用-actions-更新-state"><span class="nav-number">2.2.</span> <span class="nav-text">使用 actions 更新 state</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#使用-action-包装函数"><span class="nav-number">2.2.1.</span> <span class="nav-text">使用 action 包装函数</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Actions-和继承"><span class="nav-number">2.2.2.</span> <span class="nav-text">Actions 和继承</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#异步-actions"><span class="nav-number">2.2.2.1.</span> <span class="nav-text">异步 actions</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#使用-flow-代替-async-await-🚀"><span class="nav-number">2.2.2.2.</span> <span class="nav-text">使用 flow 代替 async &#x2F; await {🚀}</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#通过-computeds-派生信息"><span class="nav-number">2.3.</span> <span class="nav-text">通过 computeds 派生信息</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Options-🚀-1"><span class="nav-number">2.3.1.</span> <span class="nav-text">Options {🚀}</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#使用-reactions-处理副作用"><span class="nav-number">2.4.</span> <span class="nav-text">使用 reactions 处理副作用</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Always-dispose-of-reactions"><span class="nav-number">2.4.1.</span> <span class="nav-text">Always dispose of reactions</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#谨慎地使用-reactions！"><span class="nav-number">2.4.2.</span> <span class="nav-text">谨慎地使用 reactions！</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Options-🚀-2"><span class="nav-number">2.4.3.</span> <span class="nav-text">Options {🚀}</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Mobx-and-React"><span class="nav-number">3.</span> <span class="nav-text">Mobx and React</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#集成React（react-integration）"><span class="nav-number">3.1.</span> <span class="nav-text">集成React（react-integration）</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#本地与外部状态（Local-and-external-state）"><span class="nav-number">3.1.1.</span> <span class="nav-text">本地与外部状态（Local and external state）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#小贴士-尽可能晚地从对象中获取值"><span class="nav-number">3.1.2.</span> <span class="nav-text">小贴士: 尽可能晚地从对象中获取值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#不要将可观察对象传递到-不是observer的组件中（Don’t-pass-observables-into-components-that-aren’t-observer）"><span class="nav-number">3.1.3.</span> <span class="nav-text">不要将可观察对象传递到 不是observer的组件中（Don’t pass observables into components that aren’t observer）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#回调组件可能会需要-lt-Observer-gt-（-Callback-components-might-require-lt-Observer-gt-）"><span class="nav-number">3.1.4.</span> <span class="nav-text">回调组件可能会需要&lt;Observer&gt;（ Callback components might require &lt;Observer&gt;）</span></a></li></ol></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Li Yudong"
      src="/images/author.jpg">
  <p class="site-author-name" itemprop="name">Li Yudong</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">95</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">18</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/tiarmor1" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;tiarmor1" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:1157019137@qq.com" title="E-Mail → mailto:1157019137@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>
  <div class="cc-license motion-element" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 2020 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Li Yudong</span>
</div>

        








      </div>
    </footer>
  </div>

  
  
  <script color='0,0,0' opacity='0.5' zIndex='-1' count='150' src="/lib1/canvas-nest/canvas-nest.min.js"></script>
  <script src="/lib1/anime.min.js"></script>
  <script src="/lib1/velocity/velocity.min.js"></script>
  <script src="/lib1/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  




  
<script src="/js/local-search.js"></script>













  

  

  
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" src="/js/fireworks.js"></script>
  
</body>
</html>

